/*
** Xin YUAN, 2023, BSD (2)
*/

////////////////////////////////////////////////////////////////////////////////

// CRC32Util
/*! \brief A utility class for CRC32 calculation.

A utility class for CRC32 calculation.
*/
class CRC32Util
{
protected:
	/*! \brief Two CRC constants.

	Two CRC constants.
	*/
	enum {
		CRC_MASK = 0xFFFFFFFFUL,            //!< CRC mask
		CRC_32_POLYNORMAL = 0xEDB88320UL,   //!< CRC polynormal
		CRC_FIRST = -1L                     //!< CRC initial value
	};

public:
	/*! \brief Build CRC32 table.

	Build CRC32 table.
	*/
	void BuildTable() throw()
	{
		//Initialize
		uint32_t i, j;
		uint32_t value;
		for(i = 0; i < 256; i ++) {
			value = i;
			for(j = 8; j > 0; j --) {
				if(NumberTraits<uint32_t>::IsOdd(value))
					value = (value >> 1) ^ CRC_32_POLYNORMAL;
				else
					value >>= 1;
			}
			m_table[i] = value;
		} //end for
	}

protected:
	/*! \brief Update CRC32 value accroding to input buffer.

	Update CRC32 value accroding to input buffer.
	\param lpBuf [in] A pointer to buffer.
	\param uCount [in] The byte count of buffer.
	\param uCRC [in] The current CRC32 value.
	\return The updated CRC32 value.
	*/
	uint32_t update_block_crc32(const uint8_t* lpBuf, uint32_t uCount, uint32_t uCRC) const throw()
	{
		const uint8_t* p = lpBuf;
		uint32_t tp1, tp2;

		while( uCount-- != 0 ) {
			tp1 = (uCRC >> 8) & 0x00FFFFFFUL;
			tp2 = m_table[ (uCRC ^ ((uint32_t)(*p))) & 0x000000FFUL ];
			uCRC = tp1 ^ tp2;
			++ p;
		} //end while

		return uCRC;
	}

protected:
	/*! \brief CRC32 table.

	CRC32 table.
	*/
	uint32_t m_table[256];  //!< CRC32 table

private:
	friend class CRC32Hash;
};

// CRC32Hash
/*! \brief A class for CRC32 calculation.

A class for CRC32 calculation.
*/
class CRC32Hash
{

public:
	/*! \brief Constructor.

	Constructor.
	*/
	CRC32Hash() throw() : m_uCRCValue((uint32_t)CRC32Util::CRC_FIRST)
	{
	}

	/*! \brief Initialize the CRC32 calculation.

	Initialize the CRC32 calculation.
	\note This method must be called firstly when beginning a new calculation process.
	*/
	void Initialize() throw()
	{
		m_uCRCValue = (uint32_t)CRC32Util::CRC_FIRST;
	}

	/*! \brief Input data to update the CRC32 value.

	Input data to update the CRC32 value.
	\param util [in] A utility object.
	\param lpBuf [in] A pointer to buffer.
	\param uCount [in] The byte count of buffer.
	*/
	void InputData(const CRC32Util& util, const uint8_t* lpBuf, uint32_t uCount) throw()
	{
		m_uCRCValue = util.update_block_crc32(lpBuf, uCount, m_uCRCValue);
	}

	/*! \brief Get the result CRC32 value.

	Get the result CRC32 value.
	\return The result CRC32 value.
	*/
	uint32_t GetValue() throw()
	{
		return m_uCRCValue ^ CRC32Util::CRC_MASK;
	}

protected:
	uint32_t m_uCRCValue;  //!< CRC value
};

////////////////////////////////////////////////////////////////////////////////
